package jee.demo.pro.dao;

import org.hibernate.LockOptions;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.List;


/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 2010-1-28
 * Time: 11:42:51
 */
@SuppressWarnings("unchecked")
public abstract class AbstractDAO<T> implements DAO<T> {
    protected static Logger LOGGER = LoggerFactory.getLogger(AbstractDAO.class);
    protected String tableName;
    protected String entityName;
    private Class<T> entityClass;

    @Resource
    SessionFactory sessionFactory;

    public AbstractDAO(String tableName, Class<T> entityClass) {
        this.tableName = tableName;
        this.entityName = entityClass.getName();
        this.entityClass = entityClass;
    }

    
    public AbstractDAO(String tableName, String entityName) {
        this.tableName = tableName;
        this.entityName = entityName;
        /*entityClass =(Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];*/
        try {
            entityClass = (Class<T>) Class.forName(entityName);
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
    }

    public Integer save(Object o) {
        LOGGER.info("saving " + o.getClass().getName() + " instance");
        Integer id = (Integer) sessionFactory.getCurrentSession().save(o);
        LOGGER.info("save successful");
        return id;
    }

    public void delete(T t) {
        LOGGER.info("deleting " + entityName + " instance");
        sessionFactory.getCurrentSession().delete(t);
        LOGGER.info("delete successful");
    }

    
    public T merge(T t) {
        LOGGER.info("merging " + entityName + " instance");
        return (T) sessionFactory.getCurrentSession().merge(t);
    }

    public void update(T t) {
        LOGGER.info("updating " + entityName + " instance");
        sessionFactory.getCurrentSession().update(t);
        LOGGER.info("update successful");
    }

    public void attachDirty(T instance) {
        LOGGER.info("attaching dirty " + entityName + " instance");
        sessionFactory.getCurrentSession().saveOrUpdate(instance);
        LOGGER.info("attach successful");
    }

    public void attachClean(T instance) {
        LOGGER.info("attaching clean " + entityName + " instance");
        sessionFactory.getCurrentSession().buildLockRequest(LockOptions.NONE).lock(instance);
        LOGGER.info("attach successful");
    }

    
    public T getById(Integer id) {
        LOGGER.info("getting " + entityName + " instance with id: " + id);
        return (T) sessionFactory.getCurrentSession().get(entityClass, id);
    }

    
    public T loadById(Integer id) {
        LOGGER.info("loading " + entityName + " instance with id: " + id);
        return (T) sessionFactory.getCurrentSession().load(entityClass, id);
    }

    
    public T uniqueByProperty(final String propertyName, final Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + propertyName + ", value: " + value);
        Query query = sessionFactory.getCurrentSession().createQuery("from " + tableName + " where " + propertyName + "=?");
        query.setParameter(0, value);
        return (T) query.uniqueResult();
    }

    public Long countByProperty(String key, Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + key + ", value: " + value);
        Query query = sessionFactory.getCurrentSession().createQuery("select count(*) from " + tableName + " where " + key + "=?");
        query.setParameter(0, value);
        return (Long) query.uniqueResult();
    }

    
    public List<T> findByProperty(String propertyName, Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + propertyName + ", value: " + value);
        Query query = sessionFactory.getCurrentSession().createQuery("from " + tableName + " where " + propertyName + "=?");
        query.setParameter(0, value);
        return query.list();
    }

    public List<T> findByNamedQuery(String queryName) {
        return findByNamedQuery(queryName, null);
    }

    public List<T> findByNamedQuery(String queryName, Object value) {
        return findByNamedQuery(queryName, new Object[]{value});
    }

    public List<T> findByNamedQuery(String queryName, Object[] values) {
        LOGGER.info("finding with NamedQuery: " + queryName);
        Query query = sessionFactory.getCurrentSession().getNamedQuery(queryName);

        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    public int updateByNamedQuery(String queryName) {
        return updateByNamedQuery(queryName, null);
    }

    public int updateByNamedQuery(String queryName, Object value) {
        return updateByNamedQuery(queryName, new Object[]{value});
    }

    /**
     * 该方法出现问题的话参照 sessionFactory.getCurrentSession().findByNamedQuery 修改
     *
     */
    public int updateByNamedQuery(final String queryName, final Object[] values) {
        LOGGER.info("updating with NamedQuery: " + queryName);
        Query query = sessionFactory.getCurrentSession().getNamedQuery(queryName);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.executeUpdate();
    }

    public List<T> pageByNamedQuery(String queryName, int start, int limit) {
        return pageByNamedQuery(queryName, null, start, limit);
    }

    public List<T> pageByNamedQuery(String queryName, Object value, int start, int limit) {
        return pageByNamedQuery(queryName, new Object[]{value}, start, limit);
    }

    public List<T> pageByNamedQuery(String queryName, Object[] values, int start, int limit) {
        LOGGER.info("finding with NamedQuery: " + queryName
                + " and paging with start: " + start + ", limit: " + limit);
        Query query = sessionFactory.getCurrentSession().getNamedQuery(queryName);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        if (start > 0) {
            query.setFirstResult(start);
        }
        if (limit > 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }


    public List<T> findColumn(String columnName) {
        return findColumns(new String[]{columnName});
    }

    public List<T> findColumns(String columnNames[]) {
        StringBuffer columns = new StringBuffer();
        if (columnNames.length > 0) {
            columns.append(columnNames[0]);
            for (int i = 1; i < columnNames.length; i++) {
                columns.append(", ").append(columnNames[i]);
            }
        }
        LOGGER.info("finding column: " + columns + " of " + entityName + " instance");
        StringBuilder queryString = new StringBuilder();
        if (columnNames.length > 0) {
            queryString.append("select ").append(columns);
        }
        queryString.append(" from ").append(tableName);
        /*if ( null != id ){
             queryString.append(" where id = ").append(tableName);
        }*/
        return sessionFactory.getCurrentSession().getNamedQuery(queryString.toString()).list();
    }

    public List<T> page(int start, int limit) {
        LOGGER.info("finding " + limit + " " + entityName + " instances from "
                + (start + 1) + "(th) instance");
        return pageByProperty(null, null, start, limit);
    }

    
    public List<T> pageByProperty(String propertyName, Object value, int start, int limit) {
        StringBuilder queryString = new StringBuilder();
        queryString.append("from ")
                .append(tableName);
        if (null != propertyName) {
            LOGGER.info("finding " + limit + " " + entityName + " instances from "
                    + (start + 1) + "(th) instance with property: " + propertyName
                    + ", value: " + value);
            queryString.append(" where ").append(propertyName).append("=?");
        }
        Query query = sessionFactory.getCurrentSession().createQuery(queryString.toString());
        if (null != value) {
            query.setParameter(0, value);
        }
        if (start > 0) {
            query.setFirstResult(start);
        }
        if (limit > 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }

    public List<T> findAll() {
        LOGGER.info("finding all " + entityName + " instances");
        return sessionFactory.getCurrentSession().createQuery("from " + tableName).list();
    }

    public final long totalByPropertyInTable(String cTableName, String propertyName, Object value) {
        LOGGER.info("check if exists with property: " + propertyName
                + ", value: " + value + "in table:" + cTableName);
        Query query = sessionFactory.getCurrentSession().createQuery("select count(*) from " + cTableName + " where " + propertyName + " = ?");
        query.setParameter(0, value);
        return (Long) query.uniqueResult();
    }

    /*public Object getByIdInTable(String cTableName, Object id){
        LOGGER.info("check if exists id: " + id + " in table:" + cTableName);
        StringBuffer querySB = new StringBuffer();
        querySB.append("from ").append(cTableName).append(" where id=?");
        List<T> List<T> = sessionFactory.getCurrentSession().find(querySB.toString(), id);
        return list.size() == 0 ? null : list.get(0); 
    }   */

    public T getByIdInTable(String cTableName, Object id) {
        LOGGER.info("check if exists id[" + id + "] in table:" + cTableName);
        StringBuilder querySB = new StringBuilder();
        querySB.append("from ").append(cTableName).append(" where id = ?");
        Query query = sessionFactory.getCurrentSession().createQuery(querySB.toString());
        query.setParameter(0, id);
        return (T) query.uniqueResult();
    }

    public long getTotalResults() {
        LOGGER.info("finding total results of " + entityName + " instances");
        return (Long) sessionFactory.getCurrentSession().createQuery("select count(*) from " + tableName).uniqueResult();
    }
}
